package main

import (
	"github.com/fsnotify/fsnotify"
	"github.com/shirou/gopsutil/process"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"
)

var basePath = "C:\\ProgramData\\PopCap Games\\PlantsVsZombies\\pvzHE\\yourdata"
var backPath = basePath + "\\backup"

var pvzPid int32 = -1
var pvzProcessName = "PlantsVsZombies.exe"

func main() {
	openGame()
	setDaemonProcess()
	initSaveDir()
	watchDir()

}

/*
*
监听
*/
func watchDir() {
	// Create new watcher.
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}
	defer watcher.Close()

	// Start listening for events.
	go func() {
		for {
			select {
			case event, ok := <-watcher.Events:
				if !ok {
					return
				}
				log.Println("event:", event)
				if event.Has(fsnotify.Write) {
					log.Println("modified file:", event.Name)
					//过滤指定路径下的文件
					s := strings.Split(event.Name, "\\")
					if s[len(s)-2] == "yourdata" {
						backupFile(event.Name)
					}
				}
			case err, ok := <-watcher.Errors:
				if !ok {
					return
				}
				log.Println("error:", err)
			}
		}
	}()

	// Add a path.
	err = watcher.Add(basePath)
	if err != nil {
		log.Fatal(err)
	}

	// Block main goroutine forever.
	<-make(chan struct{})
}

func initSaveDir() {
	_, err := os.Stat(backPath)
	if err != nil {
		//不存在文件夹
		err := os.MkdirAll(backPath, 0750)
		if err != nil {
			log.Println("文件夹创建失败！")
			return
		}
	} else {
		//存在则从备份同步文件到主文件夹
		files, _ := os.ReadDir(backPath)
		for _, v := range files {
			syncFile(v.Name())
		}
	}
}

/*
*
yourdata -> backup
*/
func backupFile(filePath string) {
	file, err := os.OpenFile(filePath, os.O_RDWR, 0666)
	defer file.Close()
	if err != nil {
		return
	}
	stat, _ := file.Stat()
	size := stat.Size()
	buf := make([]byte, size)
	_, rErr := file.Read(buf)
	if rErr != nil {
		log.Println("文件读取失败！")
		return
	}
	sArray := strings.Split(filePath, "\\")
	fileName := "\\" + sArray[len(sArray)-1]
	f, err := os.OpenFile(backPath+fileName, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm)
	defer f.Close()
	_, wErr := f.Write(buf)
	if wErr != nil {
		log.Println("文件写入失败！")
		return
	}
	log.Println("备份成功！")
}

/*
*
backup -> yourdata
*/
func syncFile(fileName string) {
	filePath := backPath + "\\" + fileName
	file, err := os.OpenFile(filePath, os.O_RDONLY, os.ModePerm)
	defer file.Close()
	if err != nil {
		log.Println("还原数据失败！")
		return
	}
	stat, _ := file.Stat()
	size := stat.Size()
	buf := make([]byte, size)
	file.Read(buf)
	//outfile
	target, err := os.OpenFile(basePath+"\\"+fileName, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm)
	target.Write(buf)
	defer target.Close()
	log.Println("数据还原成功！")
}

/*
*
启动游戏
*/
func openGame() {
	go func() {
		cmd := exec.Command(".\\pvzHE-Launcher.exe")
		cmd.Run()
	}()
}

/*
*
设置为守护进程
*/
func setDaemonProcess() {
	//等待启动完成
	time.Sleep(2 * time.Second)
	// find pvz id
	pMap := getProcessName()
	for pName, pid := range pMap {
		if pName == pvzProcessName {
			pvzPid = pid
			break
		}
	}
	if pvzPid == -1 {
		log.Println("未找到对应进程！")
	}

	tickerChannel := make(chan bool)
	interval := 2 * time.Second

	go timer(tickerChannel, interval)
	go func() {
		for {
			select {
			case <-tickerChannel:
				//循环检测进程存活
				isExists, err := process.PidExists(pvzPid)
				if err != nil {
					log.Println(err.Error())
				}
				if !isExists {
					//进程未存活-退出
					os.Exit(0)
				}
			}
		}
	}()
}

/*
*
get pid
*/
func getProcessName() map[string]int32 {

	pidMap := make(map[string]int32)

	pids, _ := process.Pids()
	for _, pid := range pids {
		pn, _ := process.NewProcess(pid)
		pName, _ := pn.Name()
		pidMap[pName] = pid
	}
	return pidMap
}

// 定时器函数，接收一个通道和一个时间间隔
func timer(ch chan<- bool, interval time.Duration) {
	for {
		time.Sleep(interval)
		ch <- true
	}
}
